package es.deusto.ingenieria.is.search.tsp.formulation;
import java.util.ArrayList;
import java.util.List;

import es.deusto.ingenieria.is.search.formulation.*;



/**Objeto que representa el entrono de nuestro problema.
 * @author Kevin e Inigo
 *
 */
public class EnvironmentMap extends State{

	
	/**
	 * Variable para comprobar que la distancia total corresponde
	 */
	public double costeTotal=0;
	/**
	 * Numero de ciudades totales del entorno
	 */
	public int numCiudad;
	/**
	 * Clase ciudad que contiene la ciudad de inicio del entrono.
	 */
	public Ciudad cInicial;

	/**
	 * Clase ciudad que contiene la ciudad de fin del entrono
	 */
	public Ciudad cFinal;

	/**
	 * Objeto lista que almacena las ciudades visitadas hasta el momento para un entorno determindado. Por defecto, la ciudad inciio consideramos
	 * como visitada
	 */
	public List<Ciudad> ciudadesVisitadas;

	/**
	 * Objeto lista que almacena las ciudades no visitadas hasta el momento para un entorno determindado. 
	 * 
	 */
	public List<Ciudad> ciudadesNoVisitadas;

	/**Constructor del Entorno, recibe el numero de ciudades del entorno, la lista de ciudades sin visitar , las no visitadas, 
	 * y la ciudad inicial y final.
	 * @param numeroCiudades
	 * @param ciudades
	 * @param ciudadInicio
	 * @param ciudadFinal
	 */
	public EnvironmentMap(int numeroCiudades, ArrayList<Ciudad>ciudades, Ciudad ciudadInicio, Ciudad ciudadFinal) {
		// TODO Auto-generated constructor stub
		ciudadesNoVisitadas=new ArrayList<Ciudad>();
		ciudadesVisitadas= new ArrayList<Ciudad>();
		this.numCiudad = numeroCiudades;
		cInicial = ciudadInicio;
		cFinal = ciudadFinal;

		for(int i = 0; i < ciudades.size(); i ++){
				ciudadesNoVisitadas.add(ciudades.get(i));
		}

	}


	/**Constructor del Entorno en el que no pasamos los arraylist.
	 * @param numCiudad
	 * @param cInicial
	 * @param cFinal
	 */
	public EnvironmentMap(int numCiudad, Ciudad cInicial, Ciudad cFinal) {
		super();
		this.numCiudad = numCiudad;
		this.cInicial = cInicial;
		this.cFinal = cFinal;
	}




	/** Devuelve el ArrayList de las ciudades que ya han sido visitadas
	 * @return List<Ciudad> ciudadesVisitadas
	 */
	public List<Ciudad> getCiudadesVisitadas() {
		return ciudadesVisitadas;
	}


	/** Introduce en el ArrayList del enviroment las ciudades visitadas pasadas como parametro.
	 * @param ciudadesVisitadas
	 */
	public void setCiudadesVisitadas(List<Ciudad> ciudadesVisitadas) {
		this.ciudadesVisitadas = ciudadesVisitadas;
	}


	/**Devuelve el ArrayList de las ciudades que no han sido visitadas
	 * @return List<Ciudad> ciudadesNoVisitadas
	 */
	public List<Ciudad> getCiudadesNoVisitadas() {
		return ciudadesNoVisitadas;
	}

	/** Introduce en el ArrayList del enviroment las ciudades no visitadas pasadas como parametro.
	 * @param ciudadesNoVisitadas
	 */
	public void setCiudadesNoVisitadas(List<Ciudad> ciudadesNoVisitadas) {
		this.ciudadesNoVisitadas = ciudadesNoVisitadas;
	}


	/* (non-Javadoc)
	 * @see es.deusto.ingenieria.is.search.formulation.State#equals(java.lang.Object)
	 * Compara si dos entornos son similares. Para ello va comprobando el orden de las ciudades visitadas, en cuanto encuentra una 
	 * diferente devuelve false. 
	 */
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if ( obj != null && obj instanceof EnvironmentMap){

			EnvironmentMap enviromentAux = (EnvironmentMap)obj;
			return ciudadesVisitadas.equals(enviromentAux.ciudadesVisitadas);
	
		} 
		return false;
	}

	/* (non-Javadoc)
	 * @see es.deusto.ingenieria.is.search.formulation.State#toString()
	 * Metodo que muestra la informacion principal del entorno, como puede ser las ciudades visitadas, ciudades no visitadas.
	 */
	@Override
	public String toString() {
				String texto="";
//				texto=texto+"*Numero de ciudades: "+numCiudad+"\n";
//				texto=texto+"\n*Ciudad de Inicio:";
//				texto=texto+"\n"+cInicial;
//				texto=texto+"\n*Ciudad de Final:";
//				texto=texto+"\n"+cFinal+"\n";
//				
				//texto = texto+"\n*Ciudades Visitadas: \n";
				int i = 0;
				
				while(i<ciudadesVisitadas.size()){
					texto = texto + ciudadesVisitadas.get(i).toString()+"";
					i++;
				}
	    /*
		texto = texto+"\n*Ciudades No Visitadas: \n";
		i = 0;

		while(i<ciudadesNoVisitadas.size()){
			texto = texto +" "+ ciudadesNoVisitadas.get(i).toString()+" ";
			i++;
		}*/
		if(ciudadesVisitadas.size()==0)
		{
			texto=texto+cInicial.toString();
		}
		return texto;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 * Metodo que copia el contenido de un enviromente a otro. Para ello inicialmente crea un objeto EnvirmoentMap pasandole el numero de ciudades,
	 * ciudad inicial y final del enviroment que queremos clonar. A continuacion, vamos anadiendo a cada array(Visitadas y NoVisitadas) 
	 * las ciudades que correspondan.
	 */
	@SuppressWarnings("unchecked")
	public Object clone() {
		
		EnvironmentMap clon = new EnvironmentMap(this.numCiudad, this.cInicial, this.cFinal);

		try {
			
			clon.ciudadesNoVisitadas = (ArrayList<Ciudad>) ((ArrayList<Ciudad>) ciudadesNoVisitadas).clone();
			clon.ciudadesVisitadas = (ArrayList<Ciudad>) ((ArrayList<Ciudad>) ciudadesVisitadas).clone();
			clon.costeTotal=this.costeTotal;
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("% [ERROR] Environment.clone(): " + e.getMessage());
		}
		System.out.println(clon);
		return clon;
	}


	/** Quitamos del ArrayList de ciudades sin visitar la ciudad pasada como parametro
	 * @param Ciudad ciudadAnadir
	 */
	public void borrarCiudad(Ciudad ciudadAnadir) {
		ciudadesNoVisitadas.remove(ciudadAnadir);	
	}


	/** Anadimos al ArrayList de ciudades visitadas la ciudad pasada como parametro
	 * @param Ciudad ciudadAnadir
	 */
	public void anadirCiudad(Ciudad ciudadAnadir) {
		ciudadesVisitadas.add(ciudadAnadir);		
	}

	/** Comprueba si la ciudad que queremos anadir ya ha sido visitada
	 * @param ciudadAnadir
	 * @return boolean ciudadVisitada
	 */
	public boolean comprobarCiudad(Ciudad ciudadAnadir){
		return ciudadesVisitadas.contains(ciudadAnadir);
	}
	
	
	/** Devuelve el coste desde la ultima ciudad visitada hasta la ciudad final. Este metodo representa la heuristica que hemos seleccionado:
	 * Distancia desde la ultima ciudad visitda hasta la ciudad final en linea recta.
	 * @return costeHastaCiudadFinal
	 */
	public double getEnLineaRecta() {
		Ciudad c=ciudadesVisitadas.get(ciudadesVisitadas.size()-1);
		double xtemp = Math.pow(cFinal.x-c.x,2);
		double ytemp = Math.pow(cFinal.y-c.y,2);
		return Math.sqrt(xtemp + ytemp);
	}


	/** Devuelve la distancia desde el nodo incial hasta el ultimo nodo visitado en linea recta
	 * @return distanciaTotal
	 */
	public double getDistanciaLineaRecta() {
		boolean primero=true;
		double distanciaTotal=0;
		Ciudad temp=null;
		for(Ciudad c: ciudadesVisitadas)
		{
			if(primero)
			{
				double xtemp = Math.pow(c.x-cInicial.x,2);
				double ytemp = Math.pow(c.y-cInicial.y,2);
				distanciaTotal=distanciaTotal+ Math.sqrt(xtemp + ytemp);
				primero=false;
			}
			else
			{
				double xtemp = Math.pow(c.x-temp.x,2);
				double ytemp = Math.pow(c.y-temp.y,2);
				distanciaTotal=distanciaTotal+ Math.sqrt(xtemp + ytemp);
			}
			temp=c;
		}
		return distanciaTotal;
	}
	
	
	/**Devuelve la distancia desde el nodo incial hasta el ultimo nodo visitado mediante Manhattan
	 * @return distanciaTotal
	 */
	public double getDistanciaManhattan() {
		boolean primero=true;
		double distanciaTotal=0;
		Ciudad temp=null;
		for(Ciudad c: ciudadesVisitadas)
		{
			if(primero)
			{
				distanciaTotal=distanciaTotal+ (Math.abs(cInicial.x- c.x) + Math.abs(cInicial.y - c.y));	
				primero=false;
			}
			else
			{
				distanciaTotal=distanciaTotal+ (Math.abs(temp.x - c.x) + Math.abs(temp.y - c.y));	
			}
			temp=c;
		}
		return distanciaTotal;
	}
	
	/** Devuelve la distancia desde el nodo incial hasta el ultimo nodo visitado, y de este al nodo final mediante Manhattan
	 * @return distanciaTotal
	 */
	public double getDistanciaManhattanTotal() {
		boolean primero=true;
		double distanciaTotal=0;
		Ciudad temp=null;
		for(Ciudad c: ciudadesVisitadas)
		{
			if(primero)
			{
				distanciaTotal=distanciaTotal+ (Math.abs(cInicial.x- c.x) + Math.abs(cInicial.y - c.y));	
				primero=false;
			}
			else
			{
				distanciaTotal=distanciaTotal+ (Math.abs(temp.x - c.x) + Math.abs(temp.y - c.y));	
			}
			temp=c;
		}
		distanciaTotal=distanciaTotal+ (Math.abs(cFinal.x- temp.x) + Math.abs(cFinal.y - temp.y));
		return distanciaTotal;
	}
	

	/*
	public double getCiudadMenorCoste() {
		Ciudad c;
		if(!ciudadesVisitadas.isEmpty())
		{
		c=ciudadesVisitadas.get(ciudadesVisitadas.size()-1);
		}
		else
		{
			c=cInicial;
		}
		double costeMejor=0;
		for(Ciudad temp: ciudadesNoVisitadas)
		{
			double xtemp = Math.pow(temp.x-c.x,2);
			double ytemp = Math.pow(temp.y-c.y,2);
			double coste = Math.sqrt(xtemp + ytemp);
			if(costeMejor>coste)
			{
				costeMejor=coste;
			}
		}
		return costeMejor;
	}*/


/*
	public double getCosteAnterior() {
		Ciudad c=cInicial;
		double xtemp;
		double ytemp;
		double coste;
		double costeTotal=0;
		for(Ciudad temp: ciudadesVisitadas)
		{
			xtemp = Math.pow(temp.x-c.x,2);
			ytemp = Math.pow(temp.y-c.y,2);
			coste = Math.sqrt(xtemp + ytemp);
			costeTotal+=coste;
			c=temp;
		}
		return costeTotal;
	}*/

}
